// ----------------------------------------
// Filename: serial.c
// Description: Code providing an abstraction for 
//				hardware and/or UART
// Author: Jon Maloney
// Date: 8-Jun-2012
//

//************************************************************
//                    Include .h files
//************************************************************
#include "Signals.h"
#include "Serial.h"
#include "microRTOS.h"
#include "mqueue.h"
#include "p24FJ64GA004.h"
#include "binary_Semaphore.h"
#include "thread.h"
#include <stdlib.h>
//************************************************************
//                    Include external references
//************************************************************

//************************************************************
//                    Define Statements
//************************************************************

/* Hardware setup. */
#define serOUTPUT						0
#define serINPUT						1
#define serHIGH_SPEED					1
#define serLOW_SPEED					0
#define serONE_STOP_BIT					0
#define serEIGHT_DATA_BITS_NO_PARITY	0
#define serEIGHT_DATA_BITS_EVEN_PARITY	1
#define serNORMAL_IDLE_STATE			0
#define serAUTO_BAUD_OFF				0
#define serLOOPBACK_OFF					0
#define serWAKE_UP_DISABLE				0
#define serNO_HARDWARE_FLOW_CONTROL		0
#define serSTANDARD_IO					0
#define serNO_IRDA						0
#define serCONTINUE_IN_IDLE_MODE		0
#define serUART_ENABLED					1
#define serINTERRUPT_ON_SINGLE_CHAR		0
#define serTX_ENABLE					1
#define serTX_POLARITY_INVERSION		0
#define serINTERRUPT_ENABLE				1
#define serINTERRUPT_DISABLE			0
#define serCLEAR_FLAG					0
#define serSET_FLAG						1

//************************************************************
//                    Struct, Union, enum Statements
//************************************************************

//************************************************************
//                    Global variables and constants
//************************************************************



//************************************************************
//                    Prototype Private Functions
//************************************************************

void RX_Interrupt( void );
void TX_Interrupt( void );

//************************************************************
//                    Implementation
//************************************************************

/* The queues used to communicate between tasks and ISR's. */
static m_QueueHandle xRxedData; 
static m_QueueHandle xDataForTx; 
static xComPortHandle serialHandle;
static kernelBASE_TYPE xTxHasEnded;
static bsem_Handle UART2Rx_Sem;
static bsem_Handle UART2Tx_Sem;
/*-----------------------------------------------------------*/

xComPortHandle xSerialPortInitMinimal( UINT32 ulWantedBaud, UINT16 uxQueueLength, UINT8 SizeOfTxData, UINT8 SizeOfRxData )
{
char cChar;

/*Setup the peripheral pin */

//Unlock Registers
	UnlockRegisters();

//OUTPUT Configuration
	//Transmit Data (fn 5) mapped to RP19
	RPOR9bits.RP19R = 5;
	//Request to send (fn 6) mapped to RP20
	RPOR10bits.RP20R = 6;

//INPUT Configuration
	//Clear to send mapped to RP21
	RPINR19bits.U2CTSR = 21;
	//Receive Data mapped to RP22
	RPINR19bits.U2RXR = 22; 

//Lock Registers
	LockRegisters();

	/* Create the queues used by the com test task. */
	xRxedData = mqueue_Create( uxQueueLength, SizeOfRxData );
	xDataForTx = mqueue_Create( uxQueueLength, SizeOfTxData );

	/* Setup the UART. */
	U2MODEbits.BRGH		= serLOW_SPEED;
	U2MODEbits.STSEL	= serONE_STOP_BIT;
	U2MODEbits.PDSEL	= serEIGHT_DATA_BITS_EVEN_PARITY;
	U2MODEbits.RXINV	= serNORMAL_IDLE_STATE;
	U2MODEbits.ABAUD	= serAUTO_BAUD_OFF;
	U2MODEbits.LPBACK	= serLOOPBACK_OFF;
	U2MODEbits.WAKE		= serWAKE_UP_DISABLE;
	U2MODEbits.UEN		= serNO_HARDWARE_FLOW_CONTROL;
	U2MODEbits.IREN		= serNO_IRDA;
	U2MODEbits.USIDL	= serCONTINUE_IN_IDLE_MODE;
	U2MODEbits.UARTEN	= serUART_ENABLED;

	U2BRG = (UINT16)(( (float)configCPU_CLOCK_HZ / ( (float)16 * (float)ulWantedBaud ) ) - (float)0.5);

	U2STAbits.URXISEL	= serINTERRUPT_ON_SINGLE_CHAR;
	U2STAbits.UTXEN		= serTX_ENABLE;
	U2STAbits.UTXINV	= serTX_POLARITY_INVERSION;
	U2STAbits.UTXISEL0	= serINTERRUPT_ON_SINGLE_CHAR;
	U2STAbits.UTXISEL1	= serINTERRUPT_ON_SINGLE_CHAR;

	/* It is assumed that this function is called prior to the scheduler being
	started.  Therefore interrupts must not be allowed to occur yet as they
	may attempt to perform a context switch. */
	ENTER_CRITICAL();

	IFS1bits.U2RXIF = serCLEAR_FLAG;
	IFS1bits.U2TXIF = serCLEAR_FLAG;
	IPC7bits.U2RXIP = 3;
	IPC7bits.U2TXIP = 3;
	IEC1bits.U2TXIE = serINTERRUPT_ENABLE;
	IEC1bits.U2RXIE = serINTERRUPT_ENABLE;

	/* Clear the Rx buffer. */
	while( U2STAbits.URXDA == serSET_FLAG )
	{
		cChar = U2RXREG;
	}

	xTxHasEnded = pdTRUE;
	serialHandle = (xComPortHandle)2;//We are using UART2

	UART2Rx_Sem = sem_Create( pdFALSE );
	UART2Tx_Sem = sem_Create( pdFALSE );

	if(Signal_CreateUART2Signal(UART2Rx_Sem, UART2Tx_Sem) == pdTRUE)
	{
		//Create threads used to handle TX and RX of UART
		Thread_Create(RX_Interrupt, NULL, 200, configKERNEL_INTERRUPT_PRIORITY);
		Thread_Create(TX_Interrupt, NULL, 200, configKERNEL_INTERRUPT_PRIORITY -1);
	}
	EXIT_CRITICAL();
	
	return serialHandle;
}

xComPortHandle xGetSerialHandle(int serialPort)
{
	/* Only one port is supported. UART 2 */
	if(serialPort == 2)
		return serialHandle;
	else
		return NULL;
}

BOOL xSerialGet( xComPortHandle pxPort, void *pRxedData, kernelTickType xBlockTime )
{
	/* Only one port is supported. */
	if(pxPort == NULL)
		return pdFAIL;

	/* Only one port is supported. */
	( void ) pxPort;

	/* Get the next data block from the buffer.  Return false if no data
	are available or arrive before xBlockTime expires. */
	if( mqueue_Get( xRxedData, pRxedData, xBlockTime ) == pdTRUE )
	{
		return pdTRUE;
	}
	else
	{
		return pdFALSE;
	}
}

BOOL xSerialPut( xComPortHandle pxPort, void * ItemPtr, kernelTickType xBlockTime )
{
	/* Only one port is supported. */
	if(pxPort == NULL)
		return pdFAIL;

	/* Only one port is supported. */
	( void ) pxPort;

	/* Return false if after the block time there is no room on the Tx queue. */
	if( mqueue_Put( xDataForTx, ItemPtr, xBlockTime ) != pdPASS )
	{
		return pdFALSE;
	}

	return pdTRUE;
}

void RX_Interrupt( void )
{
	ptr_8 RxData;
	UINT8 byteOffset = xRxedData->ItemSize;
	RxData = (ptr_8)malloc(byteOffset);
	BOOL receivedAllBytesInDataBlock = pdFALSE;
	byteOffset = 0;
	
	for(;;)
	{
		Semaphore_Wait(UART2Rx_Sem, configMAX_DELAY);
		
		//While the hardware buffer is not Empty
		while( U2STAbits.URXDA )
		{
			*(RxData + byteOffset) = U2RXREG;
			byteOffset++;
			
			if(byteOffset == xRxedData->ItemSize)
			{
				byteOffset =0;
				receivedAllBytesInDataBlock = pdTRUE;
				break;	//We have sent all the bytes in the data block
			}
		}

		if(receivedAllBytesInDataBlock == pdTRUE)
		{
			if(mqueue_Put( xRxedData, RxData, 1 ) == pdFALSE)
			{
				receivedAllBytesInDataBlock = pdFALSE;
				break;	//We have to give up receiving the buffer is full
			}
		}
		
		IFS1bits.U2RXIF = serCLEAR_FLAG;
	}
}

void TX_Interrupt( void )
{
	ptr_8 TxData;
	UINT8 byteOffset = xDataForTx->ItemSize;
	TxData = (ptr_8)malloc(byteOffset);
	BOOL sentAllBytesInDataBlock = pdTRUE;
	byteOffset = 0;
	UINT8 testSendData;
	for(;;)
	{
		if(sentAllBytesInDataBlock == pdTRUE)
		{
			//Wait for the next block of data to be placed in the queue.
			if( mqueue_Get( xDataForTx, TxData, configMAX_DELAY ) == pdTRUE )
			{
				sentAllBytesInDataBlock = pdFALSE;
				byteOffset = 0;
			}
		}
		
		// If the transmit buffer is full we cannot get the next character. 
		//	We will wait on the semaphore for a signal from the UART (hardware).
		while( !( U2STAbits.UTXBF ) && sentAllBytesInDataBlock == pdFALSE)
		{
			/* Send the next character queued for Tx. */
			
			testSendData = *(TxData + byteOffset);
			U2TXREG = testSendData;
			
			byteOffset++;
			
			if(byteOffset == xDataForTx->ItemSize)
			{
				byteOffset = 0;
				sentAllBytesInDataBlock = pdTRUE;
				break;	//We have sent all the bytes in the data block
			}
		}
		
		if(sentAllBytesInDataBlock == pdFALSE)
		{
			Semaphore_Wait( UART2Tx_Sem, configMAX_DELAY );
			//Clear the TX interrupt flag
			IFS1bits.U2TXIF = serCLEAR_FLAG;
		}
	}
}


